package modules.dimensioning.solution;

import java.util.Random;
import java.util.Vector;

import javax.naming.directory.InvalidAttributesException;

import org.geotools.geometry.jts.JTSFactoryFinder;

import modules.dimensioning.CoverageInterface;
import modules.dimensioning.LegislationInterface;

import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;

import datatypes.Antenna;

/**
 * Kern van de heristiek: voorstelling van een mogelijke oplossing
 * 
 * 
 * @author Bart Moens
 */
public class Solution implements Comparable<Solution> {

	GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory( null );
	
	protected LegislationInterface li;
	protected CoverageInterface ci;
	protected Geometry areaToCover;
	
	private PartialSolution[] partialSolutions;
	
	private int nrOfPartialSolutions = 0;
	private int totalAntennas = 0;

	boolean recalculate = true;	
	double coverageFitness = 0;
	
	public Solution(Antenna[] antennaList, Geometry areaToCover, 
			LegislationInterface li, CoverageInterface ci) throws InvalidAttributesException {
		this.areaToCover = areaToCover;
		this.li = li;
		this.ci = ci;

		//split into subproblems
		SplitSolutions ss = new SplitSolutions();
		PartialAntennaArray[] pa = ss.generatePartialSolutions(antennaList,areaToCover, 3, 3);
		partialSolutions = new PartialSolution[pa.length];
		
		//generate subsolutions
		for (int i = 0; i < pa.length; i++) {
			nrOfPartialSolutions++;
			partialSolutions[i] = new PartialSolution(pa[i].getList().toArray(new Antenna[0]),pa[i].getPartialGeometry(),this);
			totalAntennas += pa[i].getList().size();
		}
	}
	
	
    public Solution() {
		// TODO Auto-generated constructor stub
	}


	//P
    public Antenna[] getAntennas() {
		//antennaList maken
    	Antenna[] antennas = new Antenna[totalAntennas];
		int j = 0;
    	for (int i = 0; i < nrOfPartialSolutions; i++) {
    		for (Antenna a : partialSolutions[i].getAntennas()) {
    			antennas[j] = a.clone();
    			j++;
    		}
    	}
    	return antennas;
    }
	

	//P
    Vector<Antenna> lastChanges = new Vector<Antenna>();
	boolean erease = true;
    public Vector<Antenna> getLatestChanges(){
		if (erease) lastChanges = new Vector<Antenna>();
		
		erease = true;
		return lastChanges;		
	}	        

	public void addChangedAntenna(Antenna a){
		if (erease)  { 
			lastChanges = new Vector<Antenna>();
			erease = false;
		}
		lastChanges.add(a);
	}	        
	


	public Vector<Antenna> getLatestChangesVector() {
		// TODO Auto-generated method stub
		return lastChanges;
	}
	
	
	/*** GENETIC METHODS **/
	
	public void mutateAll() {
		Random r = new Random();
		for (int i = 0; i < partialSolutions.length; i++) {
			int rand = r.nextInt(partialSolutions[i].getAntennas().length);
			
			if (partialSolutions[i].isOperatorSite(rand)) 
				if (r.nextInt(100) < 60)
					continue;

			double oldFitness = partialSolutions[i].getFitnessValue();
			try {
				partialSolutions[i].setActiveAntenna(rand,!partialSolutions[i].getActive(rand));
			} catch (InvalidAttributesException e) {
				e.printStackTrace();
			}
			double newFitness = partialSolutions[i].getFitnessValue();
			if (newFitness < oldFitness){
				if (r.nextInt(100) < 95) {
					partialSolutions[i].undo();
				}
				else {
					recalculate = true;					
				}
			} else {
				recalculate = true;
			}
		}
	}

	
	
	/*** FITNESS CALCULATIONS **/
	public double getCoverageFitnessValue() {
		if (recalculate) {
			recalculate = false;
			Geometry g = null;
			int i = 0;
			while (g == null) {
				g =  geometryFactory.createGeometry(partialSolutions[i].getTotalGeometry());
				i++;
				if (i == partialSolutions.length)
					break;
			}
			for (; i < partialSolutions.length; i++) {
				if (partialSolutions[i].getTotalGeometry() == null) continue;
				g = g.union(partialSolutions[i].getTotalGeometry());
			}
	
			if (g == null) {
				coverageFitness = 0;
				return 0;
			}
	
			Geometry b = areaToCover.intersection(g);
			coverageFitness = (100* b.getArea()/areaToCover.getArea());
			return coverageFitness;
		}
		return coverageFitness;
	}


	public double getCostFitnessValue() {
		double t = 0;
		for (int i = 0; i < partialSolutions.length; i++) {
			t+= partialSolutions[i].getCostFitnessValue();
		}
		return t/partialSolutions.length; 
	}

	public int getLegislationFitnessValue() {
		return 0;
	}

	public double getFitnessValue() {		
		double a = getCoverageFitnessValue();
		return a + getCostFactor(a)*getCostFitnessValue();
	}
	
	double coverageGoal = 95;
	double coverageFrom = coverageGoal-5;

	public double getCostFactor(double a) {
		if (a < coverageFrom) {
			return 0;		
		}
		if (a > coverageGoal) {
			return 5;
		}
		double a1 = (a-coverageFrom)/(coverageGoal-coverageFrom);
		double a2 = Math.pow(a1,2);
		double a3 = a2*5;  //A3 = 0 voor 90% coverage; 100 voor 95%coverage
		return a3;
	}
	

	/*** ToString **/
	public String toString(){
		int opAct = 0;
		int opTot = 0;
		int nopAct = 0;
		int nopTot = 0;
		for (int i = 0; i < partialSolutions.length; i++) {
			opAct += partialSolutions[i].getNumberOfActiveAntennasOperator();
			opTot += partialSolutions[i].getNumberOfTotalAntennasOperator();
			nopAct += partialSolutions[i].getNumberOfActiveAntennasNonOperator();
			nopTot += partialSolutions[i].getNumberOfTotalAntennasNotOperator();
		}
		
		String s = "OperatorAntennas: " + opAct + "/" + opTot + "; Extra Antennas: " + nopAct + "/" + nopTot + "; Total: " + (opAct+nopAct) + "/"+(opTot+nopTot);
		return 	"Fit: " + (float)getFitnessValue() + 
				", Cov: " + (float)getCoverageFitnessValue()+
				"%,  cost: " + (float)(getCostFitnessValue()) + "%*" + getCostFactor(getCoverageFitnessValue()) +
				"\t("+s+")";
	}
	
    public int compareTo(Solution s) {
    	return (int)(1000*getFitnessValue() - 1000*s.getFitnessValue());
    }
	
    
    public Solution clone() {
    	Solution s = new Solution();
    	s.li = li;
    	s.ci = ci;
    	s.areaToCover = geometryFactory.createGeometry(areaToCover);
    	s.recalculate = recalculate;
    	s.coverageFitness = coverageFitness;
    	s.totalAntennas = totalAntennas;
    	s.nrOfPartialSolutions = nrOfPartialSolutions;

    	s.partialSolutions = new PartialSolution[partialSolutions.length];
    	for (int i = 0; i < partialSolutions.length; i++) {
    		s.partialSolutions[i] = partialSolutions[i].clone();
    	}
    	return s;
    	    	
    }


	public PartialSolution[] getPartialSolutions() {
		// TODO Auto-generated method stub
		return partialSolutions;
	}

	public void setPartialSolutions(PartialSolution[] partialSolutions) {
		// TODO Auto-generated method stub
		this.partialSolutions = partialSolutions;
		recalculate = true;	
	}


	public LegislationInterface getLegislationInterface() {
		return li;
	}


	public CoverageInterface getCoverageInterface() {
		return ci;
	}






}